﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Diagnostics.CodeAnalysis;
using System.Drawing.Drawing2D;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;
using DrawControl.UI.Common;
using System.Timers;
using System.Windows.Forms;

namespace DrawControl.UI.View
{
    public class BubblesControl : Control
    {
        private readonly BubblesList _bubblesList;
        public BubblesControl()
        {
            SetStyle(
        ControlStyles.ResizeRedraw
        | ControlStyles.DoubleBuffer
        | ControlStyles.UserPaint
        | ControlStyles.AllPaintingInWmPaint
        | ControlStyles.SupportsTransparentBackColor,
        true
        );
            this.BackColor= Color.Transparent;
            this.Width = this.Height = 100;
            _bubblesList = new BubblesList(this);
        }
        protected override void OnPaintBackground(PaintEventArgs pevent)
        {
            
        }
    }
    public class BubblesList 
    {
        private readonly List<Bubbles> _bubblesList;
        private readonly Random rand = new Random();
        private readonly Control _control;
        private readonly Bitmap _back;
        private readonly System.Timers.Timer _timer;
        public BubblesList(Control control)
        {
            _bubblesList = new List<Bubbles>();
            _back = Properties.Resources.back;
            _control = control;
            _control.Paint += _control_Paint;
            _control.SizeChanged += _control_SizeChanged;
            _timer = new System.Timers.Timer();
            _timer.Interval = 100;
            _timer.Elapsed += _timer_Elapsed;
            _timer.Start();
        }

        private void _control_SizeChanged(object? sender, EventArgs e)
        {
            _control.Invalidate();
        }

        private void _timer_Elapsed(object? sender, ElapsedEventArgs e)
        {
            for (int i = 0; i < _bubblesList.Count; i++)
            {
                var bubb = _bubblesList[i];
                if (bubb.Circle.Radius <= 0 || bubb.Circle.Pivot.X < 0 || bubb.Circle.Pivot.X > _control.Width || bubb.Circle.Pivot.Y + bubb.Circle.Radius * 2f < 0)
                    _bubblesList.RemoveAt(i);
                else
                {
                    bubb.Velocity = new Point(rand.Next(10), rand.Next(5,20));
                    bubb.Move();
                }
            }
            int count = 20 - _bubblesList.Count;
            for (int i = 0; i < count; i++)
            {
                float radius = rand.Next(5, 15);
                PointF pivot = new PointF(rand.Next((int)(_control.Width - radius * 2)), rand.Next(_control.Height >> 1, (int)(_control.Height - radius * 2)));
                var bubb = new Bubbles(pivot, radius);
                bubb.SetColor(Color.FromArgb(159, 206, 255));
                bubb.Velocity = new PointF(rand.Next(10), rand.Next(5,20));
                _bubblesList.Add(bubb);
            }

            _control.Invalidate();
        }

        private void _control_Paint(object? sender, PaintEventArgs e)
        {
            e.Graphics.Smooth();
            DrawBack(e);
            DrawBubbles(e);
        }
        private void DrawBubbles(PaintEventArgs e)
        {
            for (int i = 0; i < _bubblesList.Count; i++)
            {
                _bubblesList[i].Render(e.Graphics);
            }
        }
        private float _scaleX;
        private float _scaleY;
        private float _scale;
        private int _x;
        private int _y;
        private void DrawBack(PaintEventArgs e)
        {
            int destWidth = _control.Width;
            int destHeight = _control.Height;
            int srcWidth = _back.Width;
            int srcHeight = _back.Height;
            _scaleX = destWidth / (float)srcWidth;
            _scaleY = destHeight / (float)srcHeight;
            _scale = Math.Min(_scaleX, _scaleY);
            int width = (int)(srcWidth * _scale);
            int height = (int)(srcHeight * _scale);
            _x = (destWidth - width) >> 1;
            _y = (destHeight - height) >> 1;

            BufferedGraphicsContext GraphicsContext = BufferedGraphicsManager.Current;
            using BufferedGraphics myBuffer = GraphicsContext.Allocate(e.Graphics, e.ClipRectangle);
            using Graphics g1 = myBuffer.Graphics;
            g1.Clear(_control.BackColor);
            g1.DrawImage(_back, _x, _y, width, height);
            myBuffer.Render(e.Graphics);
        }
    }
    public class Bubbles
    {
        private HSB _hsbColor = new HSB(1d, 0.3d, 1d, 1d);
        private CircleF _contour;
        private PointF _velocity;// 移动方向、速度

        public Bubbles(CircleF circle)
        {
            _contour = circle;
        }
        public Bubbles(PointF pivot, float radius) : this(new CircleF(pivot, radius))
        {
        }
        public CircleF Circle => _contour;
        public PointF Velocity
        {
            get => _velocity;
            set => _velocity = value;
        }
        public void SetColor(HSB hSB)
        {
            _hsbColor = hSB;
        }
        public void SetColor(Color color)
        {
            _hsbColor = HSB.ToHSB(color);
        }
        public void Move()
        {
            var pf = new PointF(_contour.Pivot.X + Velocity.X, _contour.Pivot.Y - Velocity.Y);
            _contour.Radius += (float)Math.Sqrt(Math.Pow(_contour.Pivot.X - pf.X, 2) + Math.Pow(_contour.Pivot.Y - pf.Y, 2))/20f;
            _contour.Pivot = pf;
        }
        public void Move(Rectangle rectangle)
        {
            rectangle.Inflate(-(int)(_contour.Radius * 2), -(int)(_contour.Radius * 2));
            if (!rectangle.Contains(new Point((int)_contour.Pivot.X, (int)_contour.Pivot.Y)))
                _contour.Pivot = new PointF(Math.Max(rectangle.Left, Math.Min(_contour.Pivot.X, rectangle.Right)),
                    Math.Max(rectangle.Top, Math.Min(_contour.Pivot.Y, rectangle.Bottom)));

            if (!(_contour.Pivot.X + _velocity.X >= rectangle.X && _contour.Pivot.X + _velocity.X <= rectangle.Right))
                _velocity.X = -_velocity.X;

            if (!(_contour.Pivot.Y + _velocity.Y >= rectangle.Y && _contour.Pivot.Y + _velocity.Y <= rectangle.Bottom))
                _velocity.Y = -_velocity.Y;

            _contour.Offset(_velocity);
        }
        public void Change(float step = 0.003f)
        {
            _hsbColor = HSB.FromHSB(_hsbColor.Alpha, (_hsbColor.H + step) % 1, _hsbColor.S, _hsbColor.B);
        }
        public void Render(Graphics g)
        {
            var gs = g.Save();
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            RenderContour(g);
            RenderHighLight(g);
            g.Restore(gs);
        }
        /// <summary>
        /// 绘制泡泡的基本外形
        /// </summary>
        /// <param name="g"></param>
        private void RenderContour(Graphics g)
        {
            using GraphicsPath path = new GraphicsPath();
            path.AddEllipse(_contour);
            using PathGradientBrush pgb = new PathGradientBrush(path);
            ColorBlend blend = new ColorBlend();
            pgb.CenterPoint = new PointF(_contour.Pivot.X - _contour.Radius, _contour.Pivot.Y - _contour.Radius);
            Color rgb = _hsbColor.ToRGB();
            blend.Colors = new Color[] { Color.Transparent, rgb, Color.Transparent, Color.FromArgb(20, rgb), Color.FromArgb(127, rgb) };
            blend.Positions = new float[] { 0.0f, 0.02f, 0.13f, 0.5f, 1.0f };
            pgb.InterpolationColors = blend;
            g.FillEllipse(pgb, _contour);
        }
        private void RenderHighLight(Graphics g)
        {
            RectangleF rect = _contour.GetBounds();
            using GraphicsPath path = new GraphicsPath();
            path.AddEllipse(rect);
            using PathGradientBrush pgb = new PathGradientBrush(path);
            ColorBlend blend = new ColorBlend();
            blend.Colors = new Color[] { Color.White, Color.Transparent, Color.Transparent };
            blend.Positions = new float[] { 0, 0.2f, 1 };
            pgb.CenterPoint = _contour.Pivot;
            pgb.InterpolationColors = blend;
            //绘制上部反光
            RectangleF upper = new RectangleF(rect.X + rect.Width * 0.25f, rect.Y + rect.Height * .02f, rect.Width * 0.5f, rect.Height * 0.3f);
            g.FillEllipse(pgb, upper);
            //绘制下部反光
            RectangleF down = new RectangleF(rect.X + rect.Width * 0.15f, rect.Bottom - rect.Height * 0.52f, rect.Width * 0.7f, rect.Height * 0.5f); g.FillEllipse(pgb, down);
            //绘制高光区域
            float x = upper.Width * 0.25f;
            float y = upper.Height * 0.35f;
            upper.Inflate(-x, -y);
            upper.Offset(0, -y + 2);
            Matrix m = new Matrix();
            m.RotateAt(-60, _contour.Pivot);
            g.Transform = m;
            g.FillEllipse(Brushes.White, upper);
        }
    }

    [Serializable, TypeConverter(typeof(HSBConverter))]
    public unsafe struct HSB : IEquatable<HSB>
    {
        private double _h;// 色彩
        private double _s;// 饱和度
        private double _b;// 亮度
        private double _alpha;// 透明度
        public HSB(double a, double h, double s, double b)
        {
            this.Alpha = a;
            this.H = h;
            this.S = s;
            this.B = b;
        }
        public HSB(double h, double s, double b) : this(1d, h, s, b)
        {
        }
        public static HSB Empty = new HSB();
        public double Alpha
        {
            get => this._alpha;
            set => this._alpha = Check(value);
        }
        public double H
        {
            get => this._h;
            set => this._h = Check(value);
        }
        public double S
        {
            get => this._s;
            set => this._s = Check(value);
        }
        public double B
        {
            get => this._b;
            set => this._b = Check(value);
        }
        private double Check(double value) => value > 1 ? 1 : value < 0 ? 0 : value;

        public Color ToRGB()
        {
            int max, mid, min, alpha;
            double q;
            alpha = (int)Math.Round(this.Alpha * 255);
            max = (int)Math.Round(this.B * 255);
            min = (int)Math.Round((1.0 - this.S) * (this.B / 1.0) * 255);
            q = (max - min) / (double)255;
            Color color;
            if (this.H >= 0 && this.H <= 1 / 6d)
            {
                mid = (int)Math.Round(((this.H - 0) * q) * 1530 + min);
                color = Color.FromArgb(alpha, max, mid, min);
            }
            else if (this.H <= 1 / (double)3)
            {
                mid = (int)Math.Round(-((this.H - 1 / 6d) * q) * 1530 + max);
                color = Color.FromArgb(alpha, mid, max, min);
            }
            else if (this.H <= 0.5d)
            {
                mid = (int)Math.Round(((this.H - 1 / 3d) * q) * 1530 + min);
                color = Color.FromArgb(alpha, min, max, mid);
            }
            else if (this.H <= 2 / (double)3)
            {
                mid = (int)Math.Round(-((this.H - 0.5) * q) * 1530 + max);
                color = Color.FromArgb(alpha, min, mid, max);
            }
            else if (this.H <= 5 / (double)6)
            {
                mid = (int)Math.Round(((this.H - 2 / 3d) * q) * 1530 + min);
                color = Color.FromArgb(alpha, mid, min, max);
            }
            else if (this.H <= 1.0d)
            {
                mid = (int)Math.Round(((this.H - 5 / 6d) * q) * 1530 + max);
                color = Color.FromArgb(alpha, max, min, mid);
            }
            else
                color = Color.FromArgb(alpha, 0, 0, 0);
            return color;
        }
        public static HSB ToHSB(Color c)
        {
            HSB hsb = new HSB();
            int Max, Min, Diff, Sum;
            hsb.Alpha = c.A / 255.0d;
            //将RGB颜色的各分量最大值与最小值分别存储在Max与Min变量中
            if (c.R > c.G)
            {
                Max = c.R; Min = c.G;
            }
            else { Max = c.G; Min = c.R; }
            if (c.B > Max) Max = c.B;
            else if (c.B < Min) Min = c.B;

            Diff = Max - Min;
            Sum = Max + Min;
            hsb.B = Max / 255d;

            if (Max == 0) hsb.S = 0;
            else hsb.S = (double)Diff / Max;
            double q;
            if (Diff == 0) q = 0;
            else q = 60d / Diff;

            if (Max == c.R)
            {
                if (c.G < c.B) hsb.H = (360 + q * (c.G - c.B)) / 360d;
                else hsb.H = q * (c.G - c.B) / 360d;
            }
            else if (Max == c.G) hsb.H = (120 + q * (c.B - c.R)) / 360d;
            else if (Max == c.B) hsb.H = (240 + q * (c.R - c.G)) / 360d;
            else hsb.H = 0d;
            return hsb;
        }

        public static HSB FromHSB(double h, double s, double b) => new HSB(h, s, b);

        public static HSB FromHSB(double alpha, double h, double s, double b) => new HSB(alpha, h, s, b);

        public bool Equals(HSB other) => Equals(this, other);
        public override int GetHashCode() => (Alpha, H, S, B).GetHashCode();
        public override bool Equals([NotNullWhen(true)] object? obj)
        {
            if (obj is HSB hsb)
                return Equals(this, hsb);

            return false;
        }

        private static bool Equals(HSB left, HSB right) => left.Alpha == right.Alpha && left.H == right.H && left.S == right.S && left.B == right.B;

        public static bool operator ==(HSB left, HSB right) => Equals(left, right);
        public static bool operator !=(HSB left, HSB right) => !Equals(left, right);

    }
    public class HSBConverter : ExpandableObjectConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
        {
            if (sourceType == typeof(string))
                return true;
            return base.CanConvertFrom(context, sourceType);
        }
        public override bool CanConvertTo(ITypeDescriptorContext? context, [NotNullWhen(true)] Type? destinationType)
        {
            if (destinationType == typeof(InstanceDescriptor))
                return true;
            return base.CanConvertTo(context, destinationType);
        }
        public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
        {
            if (value is string s)
            {
                double[] values = s.Split(culture.TextInfo.ListSeparator.ToCharArray()).Select(t => double.Parse(t)).ToArray();
                return HSB.FromHSB(values[0], values[1], values[2], values[3]);
            }
            return base.ConvertFrom(context, culture, value);
        }
        public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType)
        {
            if (destinationType == null)
                throw new ArgumentNullException(nameof(destinationType));

            if (value is HSB hsb)
            {
                if (destinationType == typeof(string))
                {
                    string[] rettext;
                    if (hsb == HSB.Empty) return string.Empty;

                    if (culture == null)
                        culture = CultureInfo.CurrentCulture;

                    string text1 = culture.TextInfo.ListSeparator + " ";
                    TypeConverter converter1 = TypeDescriptor.GetConverter(typeof(double));
                    int num1 = 0;
                    rettext = new string[4];
                    rettext[num1++] = converter1.ConvertToString(context, culture, hsb.Alpha) ?? string.Empty;
                    rettext[num1++] = converter1.ConvertToString(context, culture, hsb.H) ?? string.Empty;
                    rettext[num1++] = converter1.ConvertToString(context, culture, hsb.S) ?? string.Empty;
                    rettext[num1++] = converter1.ConvertToString(context, culture, hsb.B) ?? string.Empty;
                    return string.Join(text1, rettext);
                }

                if (destinationType == typeof(InstanceDescriptor))
                {
                    MemberInfo? info = null;
                    object[] objects = null;
                    if (hsb == HSB.Empty)
                        info = typeof(HSB).GetField("Empty");
                    else
                    {
                        info = typeof(HSB).GetMethod("FromHSB", new Type[] { typeof(double), typeof(double), typeof(double), typeof(double) });
                        objects = new object[] { hsb.Alpha, hsb.H, hsb.S, hsb.B };
                    }
                    if (info != null)
                        return new InstanceDescriptor(info, objects);

                    return null;
                }
            }
            return base.ConvertTo(context, culture, value, destinationType);
        }
    }
}
